#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/epoll.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <errno.h>
#include <arpa/inet.h>
#include <mqueue.h>
#include "rtxpSession.h"
#include "log.h"
#include "ioevent.h"

typedef struct _Ioevent
{
	int event_type;       //-1:get error,0: connect event,1:io event

	int is_text;          //1:pdata is text,0:pdata is binary
	unsigned char* pdata; // data pointer for io
	int data_len;         // length of pdata
	int pos;              // already send pos in pdata  
	char key[64];         // The unique identifier of this connection
	int fd;
	int expire;
}IoEvent;

IoEvents* events[MAXCONN]; 
#define RTSPPORT 30554
FILE *fp_log;		// TODO 日志暂时直接写文件吧,有时间再搞

static int get_cpu_count()
{

	FILE *fp = popen("cat /proc/cpuinfo |grep 'processor'|wc -l", "r");
	if (!fp)
	{

		LOG("popen failed:%s\n", strerror(errno));
		return -1;
	}
	char tmp[512] = "";
	fgets(tmp, sizeof(tmp), fp);
	pclose(fp);
	return atoi(tmp);
}

static int init_server(int& thread_num)
{
	char cmd[64] = "";
	sprintf(cmd, "ulimit -n %d", MAXCONN);
	system(cmd);

	if (thread_num <= 0)
	{
		thread_num = get_cpu_count(); // get cpu count
		if (thread_num <= 0)
			thread_num = 4;
	}

	return 0;
}

static inline void set_keepalive(int fd)
{

	int keep_idle = 3600, keep_alive = 1, keep_interval = 10, keep_count = 3;
	if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &keep_alive, sizeof(keep_idle)) < 0)
	{

		LOG("setsockopt SO_KEEPALIVE failed:%s\n", strerror(errno));
		return;
	}

	if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &keep_idle, sizeof(keep_idle)) < 0)
	{

		LOG("setsockopt TCP_KEEPIDLE failed:%s\n", strerror(errno));
	}

	if (setsockopt(fd, SOL_TCP, TCP_KEEPINTVL, &keep_interval, sizeof(keep_idle)) < 0)
	{

		LOG("setsockopt TCP_KEEPIDLE failed:%s\n", strerror(errno));
	}

	if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &keep_count, sizeof(keep_idle)) < 0)
	{

		LOG("setsockopt TCP_KEEPIDLE failed:%s\n", strerror(errno));
	}

	return;
}

static inline int do_accept(int fd, struct sockaddr_in *client_addr)
{
	memset(client_addr, 0x00, sizeof(struct sockaddr_in));
	socklen_t addr_len = sizeof(struct sockaddr_in);
	int conn_fd = accept(fd, (struct sockaddr *)client_addr, &addr_len);
	if (conn_fd < 0)
	{
		LOG("accept failed:%s\n", strerror(errno));
		return -1;
	}

	return conn_fd;
}

static int create_tcp_server(int port)
{
	int sock_fd = socket(AF_INET, SOCK_STREAM, 0);
	if (sock_fd < 0)
	{
		LOG("socket failed:%s\n");
		return -1;
	}

	struct sockaddr_in localaddr;
	localaddr.sin_family = AF_INET;
	localaddr.sin_addr.s_addr = inet_addr("0.0.0.0");
	localaddr.sin_port = htons(port);
	int socklen = sizeof(localaddr);

	int option = 1;
	socklen_t optlen = sizeof(option);
	setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, (void *)&option, optlen);

	if (bind(sock_fd, (struct sockaddr *)&localaddr, socklen) < 0)
	{
		close(sock_fd);
		LOG("bind  failed:%s\n", strerror(errno));
		return -1;
	}

	if (listen(sock_fd, 256) < 0)
	{
		close(sock_fd);
		LOG("listen  faield:%s\n", strerror(errno));

		return -1;
	}

	LOG("create server success\n");

	return sock_fd;
}

void create_schedule_thread(int num);

int start_server(int num)
{
	int conn_epfd = -1; // epoll fd:connected event
	int thread_num = num;
	if (init_server(thread_num) < 0)
	{
		LOG("init server failed\n");
		return -1;
	}

	create_schedule_thread(thread_num);

	int rtsp_fd = create_tcp_server(RTSPPORT);
	if (rtsp_fd < 0)
		return -1;

	conn_epfd = epoll_create(1);
	struct epoll_event conn_event_set[1];
	modify_event(conn_epfd, rtsp_fd, EPOLLIN | EPOLLERR, EPOLL_CTL_ADD);

	while (1)
	{
		int fd_count = epoll_wait(conn_epfd, conn_event_set, sizeof(conn_event_set) / sizeof(struct epoll_event), -1);
		if (fd_count < 0)
		{
			LOG("epoll_wait failed:%s\n", strerror(errno));
			continue;
		}
		int i = 0;
		for (; i < fd_count; i++)
		{
			int fd = conn_event_set[i].data.fd;
			if (fd == rtsp_fd)
			{
				struct sockaddr_in client_addr;
				int conn_fd = do_accept(fd, &client_addr);
				if (conn_fd < 0)
					continue;

				set_keepalive(conn_fd); // TODO 瞬间6万个连接请求，该函数需要占用2s左右
				if(events[conn_fd] == nullptr){
					events[conn_fd] = new IoEvents();
				}
				if (events[conn_fd]->init_events(conn_fd, inet_ntoa(client_addr.sin_addr), 1, nullptr) < 0)
					continue;
			}
			else if (conn_event_set[i].events & EPOLLERR)
			{
				LOG("get fatal error:%s, exit epoll\n", strerror(errno));
				return -1;
			}

		} // for(i < fd_count)

	} // while(1)
}

int main()
{
	INIT_LOG("./", "rtsp_server", debug, false);
	start_server(-1);
}
